package com.runa.monitor.platinterface.netservice.server;

import com.runa.monitor.platinterface.netservice.connect.Session;
import com.runa.monitor.platinterface.netservice.equipment.NormalPlcService;
import com.runa.monitor.platinterface.netservice.handler.PlcChannelInitializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


/**
 * @author zhuj
 */
public class ConnectorServer extends Server{

    private static final Logger logger = LoggerFactory.getLogger(ConnectorServer.class);

    private static EventLoopGroup workGroup = new NioEventLoopGroup();

    private NormalPlcService service;

    public ConnectorServer(NormalPlcService service) {
        this.service = service;
        launchConnector();
    }

    private void launchConnector() {
        Bootstrap bootstrap = new Bootstrap()
                .group(workGroup)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
                .handler(new PlcChannelInitializer(service.getModbusType(), this));
        bootstrap.connect(service.getIp(), service.getPort())
                .addListener((ChannelFutureListener) future -> {
            if (future.isSuccess()) {
                logger.info("connect to ip: {}, port: {} success", service.getIp(), service.getPort());
                if (service.session() != null) {
                    service.session().linked(future.channel());
                }
                deleteThis(this);
            } else {
                logger.info("connect to ip: {}, port: {} fail !", service.getIp(), service.getPort());
                addOnly(this);
            }
        });
    }


    @Override
    public boolean shutdown() {
        try {
            if (workGroup != null) {
                workGroup.shutdownGracefully();
            }
        } catch (Exception e) {
            logger.error("ConnectorServer id:{}, close Error!", service.getId());
            return false;
        }
        return true;
    }

    public void connected() {
        service.connected();
    }

    @Override
    public void reConnect(Session session) {
        addOnly(this);
    }

    @Override
    public int hashCode() {
        String code = service.getIp().replaceAll("\\.", "")+service.getPort();
        return Integer.parseInt(code);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof ConnectorServer) {
            ConnectorServer server = (ConnectorServer) obj;
            return server.service.equals(service);
        }
        return false;
    }

    /** ---------- 主动连接的重建机制 ----------- */

    static {
        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
        executorService.scheduleAtFixedRate(new ReLinkTask(), 10, 10, TimeUnit.SECONDS);
    }

    private static final List<ConnectorServer> serverQueue = new ArrayList<>();

    static void addOnly(ConnectorServer server) {
        synchronized (serverQueue) {
            //删除已存在的
            serverQueue.remove(server);
            //重新添加
            serverQueue.add(server);
        }
    }

    static void deleteThis(ConnectorServer server) {
        synchronized (serverQueue) {
            serverQueue.remove(server);
        }
    }

    static class ReLinkTask implements Runnable {
        @Override
        public void run() {
            try {
                synchronized (serverQueue) {
                    for (ConnectorServer server : serverQueue) {
                        server.launchConnector();
                    }
                }
            } catch (Exception e) {
                logger.error("reLinkTask Error: ",e);
            }
        }
    }
}
